4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
16 namespace Microsoft
.JScript
{
18 using Microsoft
.JScript
.Vsa
;
20 using System
.Collections
;
21 using System
.Reflection
;
22 using System
.Reflection
.Emit
;
24 public sealed class Try
: AST
{
26 private TypeExpression type
;
28 private AST finally_block
;
29 private BlockScope handler_scope
;
30 private FieldInfo field
;
31 private String fieldName
;
32 private bool finallyHasControlFlowOutOfIt
;
33 private Context tryEndContext
;
35 internal Try(Context context
, AST body
, AST identifier
, TypeExpression type
, AST handler
, AST finally_block
, bool finallyHasControlFlowOutOfIt
, Context tryEndContext
)
39 this.handler
= handler
;
40 this.finally_block
= finally_block
;
41 ScriptObject current_scope
= (ScriptObject
)Globals
.ScopeStack
.Peek();
42 while (current_scope
is WithObject
) //Can only happen at run time and only if there is an eval
43 current_scope
= current_scope
.GetParent();
44 this.handler_scope
= null;
46 if (identifier
!= null){
47 this.fieldName
= identifier
.ToString();
48 this.field
= current_scope
.GetField(this.fieldName
, BindingFlags
.Public
|BindingFlags
.Instance
|BindingFlags
.Static
);
49 if (this.field
!= null){
50 if (type
== null && (field
is JSVariableField
&& field
.IsStatic
&& ((JSVariableField
)field
).type
== null) && !field
.IsLiteral
&& !field
.IsInitOnly
)
51 return; //preserve legacy semantics by using the existing variable
52 if (((IActivationObject
)current_scope
).GetLocalField(this.fieldName
) != null)
53 identifier
.context
.HandleError(JSError
.DuplicateName
, false);
55 this.handler_scope
= new BlockScope(current_scope
);
56 this.handler_scope
.catchHanderScope
= true;
57 JSVariableField f
= this.handler_scope
.AddNewField(identifier
.ToString(), Missing
.Value
, FieldAttributes
.Public
); // must be a local
58 this.field
= f
; f
.originalContext
= identifier
.context
;
59 if (identifier
.context
.document
.debugOn
&& this.field
is JSLocalField
){
60 this.handler_scope
.AddFieldForLocalScopeDebugInfo((JSLocalField
)this.field
);
63 this.finallyHasControlFlowOutOfIt
= finallyHasControlFlowOutOfIt
;
64 this.tryEndContext
= tryEndContext
;
67 internal override Object
Evaluate(){
68 int i
= Globals
.ScopeStack
.Size();
69 int j
= Globals
.CallContextStack
.Size();
75 bc
= (Completion
)this.body
.Evaluate();
77 if (this.handler
== null) throw;
79 if (this.type
!= null){
80 Type ht
= this.type
.ToType();
81 if (Typeob
.Exception
.IsAssignableFrom(ht
)){
82 if (!ht
.IsInstanceOfType(e
)) throw;
83 }else if (!ht
.IsInstanceOfType(eValue
= JScriptExceptionValue(e
, this.Engine
)))
86 eValue
= JScriptExceptionValue(e
, this.Engine
);
88 eValue
= new JScriptException(JSError
.NonClsException
);
92 Globals
.ScopeStack
.TrimToSize(i
);
93 Globals
.CallContextStack
.TrimToSize(j
);
94 if (this.handler_scope
!= null){
95 this.handler_scope
.SetParent(Globals
.ScopeStack
.Peek());
96 Globals
.ScopeStack
.Push(this.handler_scope
);
98 if (this.field
!= null)
99 this.field
.SetValue(Globals
.ScopeStack
.Peek(), eValue
);
100 bc
= (Completion
)this.handler
.Evaluate();
103 Globals
.ScopeStack
.TrimToSize(i
);
104 Globals
.CallContextStack
.TrimToSize(j
);
105 if (this.finally_block
!= null){
106 fc
= (Completion
)this.finally_block
.Evaluate();
109 if (bc
== null || fc
!= null && (fc
.Exit
> 0 || fc
.Continue
> 0 || fc
.Return
))
112 if (fc
!= null && fc
.value is Missing
)
115 Completion result
= new Completion();
116 result
.Continue
= bc
.Continue
- 1;
117 result
.Exit
= bc
.Exit
- 1;
118 result
.Return
= bc
.Return
;
119 result
.value = bc
.value;
123 internal override Context
GetFirstExecutableContext(){
124 return this.body
.GetFirstExecutableContext();
127 public static Object
JScriptExceptionValue(Object e
, VsaEngine engine
){
129 engine
= new VsaEngine(true);
130 engine
.InitVsaEngine("JS7://Microsoft.JScript.Vsa.VsaEngine", new DefaultVsaSite());
132 ErrorConstructor originalError
= engine
.Globals
.globalObject
.originalError
;
133 if (e
is JScriptException
){
134 Object
value = ((JScriptException
)e
).value;
135 if (value is Exception
|| value is Missing
|| (((JScriptException
)e
).Number
&0xFFFF) != (int)JSError
.UncaughtException
)
136 return originalError
.Construct((Exception
)e
);
137 return value; //The exception wraps a non-exception value
138 }else if (e
is StackOverflowException
)
139 return originalError
.Construct(new JScriptException(JSError
.OutOfStack
));
140 else if (e
is OutOfMemoryException
)
141 return originalError
.Construct(new JScriptException(JSError
.OutOfMemory
));
142 return originalError
.Construct(e
);
145 internal override AST
PartiallyEvaluate(){
146 if (this.type
!= null){
147 this.type
.PartiallyEvaluate();
148 ((JSVariableField
)this.field
).type
= type
;
149 }else if (this.field
is JSLocalField
)
150 ((JSLocalField
)this.field
).SetInferredType(Typeob
.Object
, null); //This should never give an error
151 ScriptObject current_scope
= Globals
.ScopeStack
.Peek();
152 while (current_scope
is WithObject
) current_scope
= current_scope
.GetParent();
153 FunctionScope scope
= null;
154 BitArray before
= null;
155 if (current_scope
is FunctionScope
){
156 scope
= (FunctionScope
)current_scope
;
157 before
= scope
.DefinedFlags
;
159 this.body
= this.body
.PartiallyEvaluate();
160 if (this.handler
!= null){
161 if (this.handler_scope
!= null)
162 Globals
.ScopeStack
.Push(this.handler_scope
);
163 if (this.field
is JSLocalField
)
164 ((JSLocalField
)this.field
).isDefined
= true;
165 this.handler
= this.handler
.PartiallyEvaluate();
166 if (this.handler_scope
!= null)
167 Globals
.ScopeStack
.Pop();
169 if (this.finally_block
!= null)
170 this.finally_block
= this.finally_block
.PartiallyEvaluate();
172 scope
.DefinedFlags
= before
;
176 public static void PushHandlerScope(VsaEngine engine
, String id
, int scopeId
){
177 engine
.PushScriptObject(new BlockScope(engine
.ScriptObjectStackTop(), id
, scopeId
));
180 internal override void TranslateToIL(ILGenerator il
, Type rtype
){
181 //This assumes that rtype == Void.class.
182 bool savedInsideProtectedRegion
= compilerGlobals
.InsideProtectedRegion
;
183 compilerGlobals
.InsideProtectedRegion
= true;
184 compilerGlobals
.BreakLabelStack
.Push(compilerGlobals
.BreakLabelStack
.Peek(0));
185 compilerGlobals
.ContinueLabelStack
.Push(compilerGlobals
.ContinueLabelStack
.Peek(0));
186 il
.BeginExceptionBlock();
187 if (this.finally_block
!= null){
188 if (this.finallyHasControlFlowOutOfIt
)
189 il
.BeginExceptionBlock();
190 if (this.handler
!= null)
191 il
.BeginExceptionBlock();
193 this.body
.TranslateToIL(il
, Typeob
.Void
);
194 if (this.tryEndContext
!= null)
195 this.tryEndContext
.EmitLineInfo(il
);
196 if (this.handler
!= null){
197 if (this.type
== null){
198 il
.BeginCatchBlock(Typeob
.Exception
);
199 this.handler
.context
.EmitLineInfo(il
);
200 this.EmitILToLoadEngine(il
);
201 il
.Emit(OpCodes
.Call
, CompilerGlobals
.jScriptExceptionValueMethod
);
203 Type filterType
= this.type
.ToType();
204 if (Typeob
.Exception
.IsAssignableFrom(filterType
)){
205 il
.BeginCatchBlock(filterType
);
206 this.handler
.context
.EmitLineInfo(il
);
209 il
.BeginExceptFilterBlock();
210 this.handler
.context
.EmitLineInfo(il
);
211 this.EmitILToLoadEngine(il
);
212 il
.Emit(OpCodes
.Call
, CompilerGlobals
.jScriptExceptionValueMethod
);
213 il
.Emit(OpCodes
.Isinst
, filterType
);
214 il
.Emit(OpCodes
.Ldnull
);
215 il
.Emit(OpCodes
.Cgt_Un
);
216 il
.BeginCatchBlock(null);
217 this.EmitILToLoadEngine(il
);
218 il
.Emit(OpCodes
.Call
, CompilerGlobals
.jScriptExceptionValueMethod
);
219 Convert
.Emit(this, il
, Typeob
.Object
, filterType
);
222 Object tok
= this.field
is JSVariableField
? ((JSVariableField
)this.field
).GetMetaData() : this.field
;
223 if (tok
is LocalBuilder
)
224 il
.Emit(OpCodes
.Stloc
, (LocalBuilder
)tok
);
225 else if (tok
is FieldInfo
)
226 il
.Emit(OpCodes
.Stsfld
, (FieldInfo
)tok
);
228 Convert
.EmitLdarg(il
, (short)tok
);
230 if (this.handler_scope
!= null){
231 if (!this.handler_scope
.isKnownAtCompileTime
){ //I.e. eval or nested func
232 this.EmitILToLoadEngine(il
);
233 il
.Emit(OpCodes
.Ldstr
, this.fieldName
);
234 ConstantWrapper
.TranslateToILInt(il
, this.handler_scope
.scopeId
);
235 il
.Emit(OpCodes
.Call
, Typeob
.Try
.GetMethod("PushHandlerScope"));
236 Globals
.ScopeStack
.Push(this.handler_scope
);
237 il
.BeginExceptionBlock();
239 il
.BeginScope(); // so that we can emit local scoped information for the handler variable
240 if (this.context
.document
.debugOn
)
241 this.handler_scope
.EmitLocalInfoForFields(il
);
243 this.handler
.TranslateToIL(il
, Typeob
.Void
);
244 if (this.handler_scope
!= null){
246 if (!this.handler_scope
.isKnownAtCompileTime
){ //I.e. eval or nested func
247 il
.BeginFinallyBlock();
248 this.EmitILToLoadEngine(il
);
249 il
.Emit(OpCodes
.Call
, CompilerGlobals
.popScriptObjectMethod
);
250 il
.Emit(OpCodes
.Pop
);
251 Globals
.ScopeStack
.Pop();
252 il
.EndExceptionBlock();
255 il
.EndExceptionBlock();
257 if (this.finally_block
!= null){
258 bool savedInsideFinally
= compilerGlobals
.InsideFinally
;
259 int savedFinallyStackTop
= compilerGlobals
.FinallyStackTop
;
260 compilerGlobals
.InsideFinally
= true;
261 compilerGlobals
.FinallyStackTop
= compilerGlobals
.BreakLabelStack
.Size();
262 il
.BeginFinallyBlock();
263 this.finally_block
.TranslateToIL(il
, Typeob
.Void
);
264 il
.EndExceptionBlock();
265 compilerGlobals
.InsideFinally
= savedInsideFinally
;
266 compilerGlobals
.FinallyStackTop
= savedFinallyStackTop
;
267 if (this.finallyHasControlFlowOutOfIt
){
268 il
.BeginCatchBlock(Typeob
.BreakOutOfFinally
);
269 il
.Emit(OpCodes
.Ldfld
, Typeob
.BreakOutOfFinally
.GetField("target"));
270 // don't need to go to 0 in the loop because 0 is the outmost block (i.e. function body)
271 // and that would generate a JIT assert because the jump is sometimes outside the function
272 for (int i
= compilerGlobals
.BreakLabelStack
.Size()-1, n
= i
; i
> 0; i
--){
273 il
.Emit(OpCodes
.Dup
);
274 ConstantWrapper
.TranslateToILInt(il
, i
);
275 Label lab
= il
.DefineLabel();
276 il
.Emit(OpCodes
.Blt_S
, lab
);
277 il
.Emit(OpCodes
.Pop
);
278 if (savedInsideFinally
&& i
< savedFinallyStackTop
)
279 il
.Emit(OpCodes
.Rethrow
);
281 il
.Emit(OpCodes
.Leave
, (Label
)compilerGlobals
.BreakLabelStack
.Peek(n
-i
));
284 il
.Emit(OpCodes
.Pop
);
285 il
.BeginCatchBlock(Typeob
.ContinueOutOfFinally
);
286 il
.Emit(OpCodes
.Ldfld
, Typeob
.ContinueOutOfFinally
.GetField("target"));
287 // don't need to go to 0 in the loop because 0 is the outmost block (i.e. function body)
288 for (int i
= compilerGlobals
.ContinueLabelStack
.Size()-1, n
= i
; i
> 0; i
--){
289 il
.Emit(OpCodes
.Dup
);
290 ConstantWrapper
.TranslateToILInt(il
, i
);
291 Label lab
= il
.DefineLabel();
292 il
.Emit(OpCodes
.Blt_S
, lab
);
293 il
.Emit(OpCodes
.Pop
);
294 if (savedInsideFinally
&& i
< savedFinallyStackTop
)
295 il
.Emit(OpCodes
.Rethrow
);
297 il
.Emit(OpCodes
.Leave
, (Label
)compilerGlobals
.ContinueLabelStack
.Peek(n
-i
));
300 il
.Emit(OpCodes
.Pop
);
301 ScriptObject scope
= Globals
.ScopeStack
.Peek();
302 while (scope
!= null && !(scope
is FunctionScope
))
303 scope
= scope
.GetParent();
304 if (scope
!= null && !savedInsideFinally
){
305 il
.BeginCatchBlock(Typeob
.ReturnOutOfFinally
);
306 il
.Emit(OpCodes
.Pop
);
307 il
.Emit(OpCodes
.Leave
, ((FunctionScope
)scope
).owner
.returnLabel
);
309 il
.EndExceptionBlock();
312 compilerGlobals
.InsideProtectedRegion
= savedInsideProtectedRegion
;
313 compilerGlobals
.BreakLabelStack
.Pop();
314 compilerGlobals
.ContinueLabelStack
.Pop();
317 internal override void TranslateToILInitializer(ILGenerator il
){
318 this.body
.TranslateToILInitializer(il
);
319 if (this.handler
!= null)
320 this.handler
.TranslateToILInitializer(il
);
321 if (this.finally_block
!= null)
322 this.finally_block
.TranslateToILInitializer(il
);